#include "Renderer/renderer3D.h"

#include "Renderer/vertexArray.h"
#include "Renderer/shader.h"
#include "Renderer/uniformBuffer.h"
#include "Renderer/renderCommand.h"

#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include "Core/utils.h"
#include "renderer3D.h"
#include "Scene/Resource_manager.h"
#include "Scene/Entity.h"

namespace CC
{

    static float vertices[] = {

        -1.0f, -1.0f, -1.0f, 0.0f, 0.0f,
        -1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
        1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
        1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
        1.0f, -1.0f, -1.0f, 1.0f, 0.0f,
        -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, // back

        -1.0f, -1.0f, 1.0f, 0.0f, 0.0f,
        1.0f, -1.0f, 1.0f, 1.0f, 0.0f,
        1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
        -1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
        -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, // front

        -1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
        -1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
        -1.0f, -1.0f, -1.0f, 0.0f, 1.0f,
        -1.0f, -1.0f, -1.0f, 0.0f, 1.0f,
        -1.0f, -1.0f, 1.0f, 0.0f, 0.0f,
        -1.0f, 1.0f, 1.0f, 1.0f, 0.0f, //left

        1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
        1.0f, -1.0f, 1.0f, 0.0f, 0.0f,
        1.0f, -1.0f, -1.0f, 0.0f, 1.0f,
        1.0f, -1.0f, -1.0f, 0.0f, 1.0f,
        1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 1.0f, 1.0f, 0.0f,//right
        

        -1.0f, -1.0f, -1.0f, 0.0f, 1.0f,
        1.0f, -1.0f, -1.0f, 1.0f, 1.0f,
        1.0f, -1.0f, 1.0f, 1.0f, 0.0f,
        1.0f, -1.0f, 1.0f, 1.0f, 0.0f,
        -1.0f, -1.0f, 1.0f, 0.0f, 0.0f,
        -1.0f, -1.0f, -1.0f, 0.0f, 1.0f,//bottom
        
        
        -1.0f, 1.0f, -1.0f, 0.0f, 1.0f,
        -1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
        1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
         1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
         1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
        -1.0f, 1.0f, -1.0f, 0.0f, 1.0f,};
    
    static float skyboxVertices[] = {
        // positions
        -1.0f,  1.0f, -1.0f,
        -1.0f, -1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,
         1.0f,  1.0f, -1.0f,
        -1.0f,  1.0f, -1.0f,

        -1.0f, -1.0f,  1.0f,
        -1.0f, -1.0f, -1.0f,
        -1.0f,  1.0f, -1.0f,
        -1.0f,  1.0f, -1.0f,
        -1.0f,  1.0f,  1.0f,
        -1.0f, -1.0f,  1.0f,

         1.0f, -1.0f, -1.0f,
         1.0f, -1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,

        -1.0f, -1.0f,  1.0f,
        -1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f, -1.0f,  1.0f,
        -1.0f, -1.0f,  1.0f,

        -1.0f,  1.0f, -1.0f,
         1.0f,  1.0f, -1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
        -1.0f,  1.0f,  1.0f,
        -1.0f,  1.0f, -1.0f,

        -1.0f, -1.0f, -1.0f,
        -1.0f, -1.0f,  1.0f,
         1.0f, -1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,
        -1.0f, -1.0f,  1.0f,
         1.0f, -1.0f,  1.0f
    };


    struct QuadVertex
    {
        glm::vec3 Position;
        glm::vec4 Color;
        glm::vec2 TexCoord;
        float TexIndex;
        float TilingFactor;

        // Editor-only
        int EntityID;
    };

    struct Entity3d
    {
        QuadVertex *EntityQuadVertex;
        glm::mat4 Model;
        DisplayModesEnum Display;
        FontComponent fontComponent;           // for font
        TransformComponent transformComponent; // for font        

        Entity3d()
        {
            Model = glm::mat4(1.0);
        }
        Entity3d(QuadVertex *q, glm::mat4 m)
        {
            EntityQuadVertex = q;
            Model = m;
        }
        ~Entity3d()
        {
            delete[] EntityQuadVertex;
        }
    };

    //struct TriangleVertex
    //{
    //    glm::vec3 Position;
    //    glm::vec4 Color;
    //    glm::vec3 Normal;
    //    glm::vec2 TexCoord;
    //    glm::vec3 Tangent;
    //    glm::vec3 Bitangent;
    //    glm::ivec4 BoneIDs;
    //    glm::vec4 Weights;
    //
    //    float TexIndex;
    //    float TilingFactor;
    //
    //    // Editor-only
    //    int EntityID;
    //};

    struct Triangle3d
    {
        glm::mat4 Model;
        MeshModelMaterialComponent MeshModelMaterial;
        DisplayModesEnum Display;
        std::vector<glm::mat4> FinalBoneMatirces;
        bool HasAnim = false;

        Triangle3d()
        {
            Model = glm::mat4(1.0);
            Display = DisplayModesEnum::DisplayModesEnum_NONE;
        }
        ~Triangle3d()
        {
        }
    };

    struct LineVertex
    {
        glm::vec3 Position;
        glm::vec4 Color;

        // Editor-only
        int EntityID;
    };

    struct SkyVertex
    {
        glm::vec3 Position;
        glm::vec4 Color;

        // Editor-only
        int EntityID;
    };

    struct Renderer3DData
    {
        static const uint32_t MaxQuads = 1;
        static const uint32_t MaxVertices = MaxQuads * 36;
        static const uint32_t MaxIndices = MaxQuads * 36;
        static const uint32_t MaxTextureSlots = 5;

        static const uint32_t MaxTriangles = 10000;
        static const uint32_t MaxTriangleVertices = MaxTriangles * 3;
        static const uint32_t MaxTriangleIndices = MaxTriangles * 3;

        VertexArray *QuadVertexArray;
        VertexArray *TriangleVertexArray;
        VertexArray *LineVertexArray;
        VertexArray *SkyVertexArray;

        VertexBuffer *QuadVertexBuffer;
        VertexBuffer *TriangleVertexBuffer;
        VertexBuffer *LineVertexBuffer;
        VertexBuffer *SkyVertexBuffer;

        Shader *QuadShader;
        Shader *TriangleShader;
        Shader *TriangleShadowShader;
        Shader *LineShader;
        Shader *SkyShader;

        uint32_t QuadIndexCount = 0;
        uint32_t TriangleIndexCount = 0;

        Entity3d *QuadVertexBufferBase = nullptr;
        Entity3d *QuadVertexBufferPtr = nullptr;

        Triangle3d Triangle3dBase[1];

        TriangleVertex *TriangleVertexBufferBase = nullptr;
        TriangleVertex *TriangleVertexBufferPtr = nullptr;

        glm::vec4 QuadVertexPositions[MaxVertices];
        glm::vec4 SkyVertexPositions[MaxVertices];

        uint32_t LineVertexCount = 0;
        LineVertex *LineVertexBufferBase = nullptr;
        LineVertex *LineVertexBufferPtr = nullptr;

        uint32_t SkyVertexCount = 0;
        SkyVertex *SkyVertexBufferBase = nullptr;
        SkyVertex *SkyVertexBufferPtr = nullptr;

        float LineWidth = 2.0f;

        Texture2D *TextureSlots[MaxTextureSlots];

        glm::vec2 TextureCoords[MaxVertices];

        Renderer3D::Statistics Stats;

        DisplayModesEnum Display = DisplayModesEnum::DisplayModesEnum_TRIANGLES;
        bool Debug = false;
        bool DebugPhysics = false;
        bool ShadowDrawState = false;

        Framebuffer *FramebufferData = nullptr;
        Framebuffer *FramebufferDataTransparent = nullptr;

        Framebuffer *FramebufferDataCustom = nullptr;

        MeshCubeMaterialComponent meshCubeMaterialComponent;

        struct CameraData
        {
            glm::mat4 ViewProjection;
            glm::mat4 Projection;
            glm::mat4 View;
            glm::vec3 Position;
        };
        CameraData CameraBuffer;
        UniformBuffer *CameraUniformBuffer;
    };

    static Renderer3DData s_Data;

    glm::vec4 Renderer3D::m_LightPos = glm::vec4(0.0);
    glm::vec4 Renderer3D::m_LightColor = glm::vec4(0.0);
    float Renderer3D::m_LightIntensity = 1.0f;
    uint32_t Renderer3D::m_ViewportWidth;
    uint32_t Renderer3D::m_ViewportHeight;
    glm::mat4 Renderer3D::m_depthBiasMVP = glm::mat4(1.0);
    glm::mat4 Renderer3D::m_depthMVP = glm::mat4(1.0);


    static uint32_t m_ViewportX;
    static uint32_t m_ViewportY;
    void Renderer3D::Init()
    {
        CC_CORE_INFO("%s\n", __FUNCTION__);
        // Lines
        s_Data.LineVertexArray = VertexArray::Create();

        s_Data.LineVertexBuffer = VertexBuffer::Create(s_Data.MaxVertices * sizeof(LineVertex));
        s_Data.LineVertexBuffer->SetLayout({{ShaderDataType::Float3, "a_Position"},
                                            {ShaderDataType::Float4, "a_Color"},
                                            {ShaderDataType::Int, "a_EntityID"}});
        s_Data.LineVertexArray->AddVertexBuffer(s_Data.LineVertexBuffer);
        s_Data.LineVertexBufferBase = new LineVertex[s_Data.MaxVertices];
        // s_Data.LineShader = Shader::Create("Renderer2D_Line", PATHCONFIG("assets/shaders/Renderer2D_Line.vert"), PATHCONFIG("assets/shaders/Renderer2D_Line.frag"));
        s_Data.LineShader = Shader::Create("Renderer2D_Line", ("Renderer2D_Line"), ("Renderer2D_Line"));

        // Quad
        s_Data.QuadVertexArray = VertexArray::Create();
        s_Data.QuadVertexBuffer = VertexBuffer::Create(s_Data.MaxVertices * sizeof(QuadVertex));
        s_Data.QuadVertexBuffer->SetLayout({{ShaderDataType::Float3, "a_Position"},
                                            {ShaderDataType::Float4, "a_Color"},
                                            {ShaderDataType::Float2, "a_TexCoord"},
                                            {ShaderDataType::Float, "a_TexIndex"},
                                            {ShaderDataType::Float, "a_TilingFactor"},
                                            {ShaderDataType::Int, "a_EntityID"}});
        s_Data.QuadVertexArray->AddVertexBuffer(s_Data.QuadVertexBuffer);

        s_Data.QuadVertexBufferBase = new Entity3d[1];
        s_Data.QuadVertexBufferBase[0].EntityQuadVertex = new QuadVertex[s_Data.MaxVertices];

        uint32_t quadIndices[s_Data.MaxIndices];
        // uint32_t triangleIndices[s_Data.MaxTriangleIndices];

        for (int i = 0; i < s_Data.MaxIndices; i++)
        {
            quadIndices[i] = i;
        }

        IndexBuffer *quadIB = IndexBuffer::Create(&quadIndices[0], s_Data.MaxIndices);
        s_Data.QuadVertexArray->SetIndexBuffer(quadIB);

        // s_Data.QuadShader = Shader::Create("Renderer3D_Quad", PATHCONFIG("assets/shaders/Renderer3D_Cube.vert"), PATHCONFIG("assets/shaders/Renderer3D_Cube.frag"));
        s_Data.QuadShader = Shader::Create("Renderer3D_Quad", ("Renderer3D_Cube"), ("Renderer3D_Cube"));

        // Triangle
        s_Data.TriangleVertexArray = VertexArray::Create();
        s_Data.TriangleVertexBuffer = VertexBuffer::Create(s_Data.MaxTriangleVertices * sizeof(TriangleVertex));
        s_Data.TriangleVertexBuffer->SetLayout({{ShaderDataType::Float3, "a_Position"},
                                                {ShaderDataType::Float4, "a_Color"},
                                                {ShaderDataType::Float3, "a_Normal"},
                                                {ShaderDataType::Float2, "a_TexCoord"},
                                                {ShaderDataType::Float3, "a_Tangent"},
                                                {ShaderDataType::Float3, "a_Bitangent"},
                                                {ShaderDataType::Int4, "a_BoneIds"},
                                                {ShaderDataType::Float4, "a_Weights"},
                                                {ShaderDataType::Float, "a_TexIndex"},
                                                {ShaderDataType::Float, "a_TilingFactor"},
                                                {ShaderDataType::Int, "a_EntityID"}});
        s_Data.TriangleVertexArray->AddVertexBuffer(s_Data.TriangleVertexBuffer);
        s_Data.TriangleVertexBufferBase = new TriangleVertex[s_Data.MaxTriangleVertices];

        CC_CORE_INFO("new Triangle3d[1]\n");
        IndexBuffer *triangleIB = IndexBuffer::Create(s_Data.MaxTriangleIndices * sizeof(uint32_t));
        s_Data.TriangleVertexArray->SetIndexBuffer(triangleIB);

        // s_Data.TriangleShader = Shader::Create("Renderer3D_Model", PATHCONFIG("assets/shaders/Renderer3D_Model.vert"), PATHCONFIG("assets/shaders/Renderer3D_Model.frag"));
        //s_Data.TriangleShader = Shader::Create("Renderer3D_Model", ("Renderer3D_Model"), ("Renderer3D_Model"));
        s_Data.TriangleShader = ResourceManager::LoadShader("Renderer3D_Model", ("Renderer3D_Model"), ("Renderer3D_Model"));

        // s_Data.TriangleShadowShader = Shader::Create("Renderer3D_Model_Shadow", PATHCONFIG("assets/shaders/Renderer3D_Model_Shadow.vert"), PATHCONFIG("assets/shaders/Renderer3D_Model_Shadow.frag"));
        s_Data.TriangleShadowShader = Shader::Create("Renderer3D_Model_Shadow", ("Renderer3D_Model_Shadow"), ("Renderer3D_Model_Shadow"));

        for (int i = 0; i < s_Data.MaxVertices; i++)
        {
            s_Data.QuadVertexPositions[i] = {vertices[i * 5], vertices[i * 5 + 1], vertices[i * 5 + 2], 1.0f};
            s_Data.TextureCoords[i] = {vertices[i * 5 + 3], vertices[i * 5 + 4]};
        }

        for (uint32_t i = 0; i < s_Data.MaxTextureSlots; i++)
        {
            s_Data.TextureSlots[i] = nullptr;
        }

        // sky
        s_Data.SkyVertexArray = VertexArray::Create();

        s_Data.SkyVertexBuffer = VertexBuffer::Create(s_Data.MaxVertices * sizeof(SkyVertex));
        s_Data.SkyVertexBuffer->SetLayout({ {ShaderDataType::Float3, "a_Position"},
                                            {ShaderDataType::Float4, "a_Color"},
                                            {ShaderDataType::Int, "a_EntityID"} });
        s_Data.SkyVertexArray->AddVertexBuffer(s_Data.SkyVertexBuffer);
        s_Data.SkyVertexBufferBase = new SkyVertex[s_Data.MaxVertices];
        for (int i = 0; i < s_Data.MaxVertices; i++)
        {
            s_Data.SkyVertexPositions[i] = { skyboxVertices[i * 3], skyboxVertices[i * 3 + 1], skyboxVertices[i * 3 + 2], 1.0f };
        }
        // s_Data.LineShader = Shader::Create("Renderer2D_Line", PATHCONFIG("assets/shaders/Renderer2D_Line.vert"), PATHCONFIG("assets/shaders/Renderer2D_Line.frag"));
        s_Data.SkyShader = Shader::Create("envShader", ("envCube"), ("envCube"));

        FramebufferSpecification fbSpec;
        // fbSpec.Attachments = {FramebufferTextureFormat::RGBA8, FramebufferTextureFormat::Depth};
        fbSpec.Attachments = {FramebufferTextureFormat::RGB, FramebufferTextureFormat::DEPTH24};
        // fbSpec.Attachments = {FramebufferTextureFormat::RGBA8};
        fbSpec.Width = 512;
        fbSpec.Height = 512;

        s_Data.FramebufferData = Framebuffer::Create(fbSpec);
        s_Data.FramebufferDataTransparent = Framebuffer::Create(fbSpec);

        // s_Data.CameraUniformBuffer = UniformBuffer::Create(sizeof(Renderer3DData::CameraData), "Camera", s_Data.QuadShader->GetProgramID());
        CC_CORE_INFO("%s\n", __FUNCTION__);
    }

    void Renderer3D::Shutdown()
    {
        delete[] s_Data.QuadVertexBufferBase;
        delete[] s_Data.TriangleVertexBufferBase;
        delete[] s_Data.LineVertexBufferBase;
        delete[] s_Data.SkyVertexBufferBase;
        delete s_Data.QuadVertexArray;
        delete s_Data.TriangleVertexArray;
        delete s_Data.LineVertexArray;
        delete s_Data.SkyVertexArray;
    }

    void Renderer3D::BeginScene(Camera *camera, const glm::mat4 &transform, Framebuffer* framebuffer, bool debugPhysics, std::string renderPass)
    {
        // CC_CORE_INFO("Renderer3D::BeginScene()");
        CC_ASSERT(camera != nullptr, "ASSERT: Renderer3D::BeginScene camera null");
        (void)transform;

        RenderCommand::SetEnableState(CapEnum_GL_DEPTH_TEST, true);

        if (!debugPhysics)
        {
            RenderCommand::SetDepthFunc(DepthFuncEnum_GL_DEPTH_TEST_FUNC_LESS);
            if (s_Data.ShadowDrawState)
            {
                if ("opaque" == renderPass) {
                    s_Data.FramebufferData->Bind();
                }
                else if ("transparent" == renderPass) {
                    s_Data.FramebufferDataTransparent->Bind();
                }                

                RenderCommand::ClearDepth();
                RenderCommand::Clear();

                RenderCommand::SetEnableState(CapEnum_CULL_FACE, true);
                RenderCommand::CullFace(CullFaceEnum::CullFaceEnum_FRONT);
            }
            else
            {
                if (framebuffer) {
                    s_Data.FramebufferDataCustom = framebuffer;
                    s_Data.FramebufferDataCustom->Bind();
                }
                else {
                    s_Data.FramebufferDataCustom = nullptr;
                    RenderCommand::SetViewport(m_ViewportX, m_ViewportY, m_ViewportWidth, m_ViewportHeight);
                }

                RenderCommand::ClearDepth();
                RenderCommand::Clear();

                RenderCommand::SetEnableState(CapEnum_CULL_FACE, true);
                RenderCommand::CullFace(CullFaceEnum::CullFaceEnum_BACK);
            }
        }
        else {
            RenderCommand::SetDepthFunc(DepthFuncEnum_GL_DEPTH_TEST_FUNC_ALWAYS);
        }

        s_Data.CameraBuffer.View = camera->GetViewMatrix();
        s_Data.CameraBuffer.Projection = camera->GetProjection();
        s_Data.CameraBuffer.ViewProjection = camera->GetViewProjectionMatrix();
        s_Data.CameraBuffer.Position = camera->GetPosition();
        // s_Data.CameraUniformBuffer->SetData(&s_Data.CameraBuffer, sizeof(Renderer3DData::CameraData));

        s_Data.QuadShader->Bind();
        s_Data.QuadShader->SetMat4("u_ViewProjection", s_Data.CameraBuffer.ViewProjection);

        s_Data.SkyShader->Bind();
        s_Data.SkyShader->SetMat4("u_Projection", s_Data.CameraBuffer.Projection);
        s_Data.SkyShader->SetMat4("u_View", glm::mat4(glm::mat3(s_Data.CameraBuffer.View)));

        s_Data.TriangleShader->Bind();
        s_Data.TriangleShader->SetMat4("u_Projection", s_Data.CameraBuffer.Projection);
        s_Data.TriangleShader->SetMat4("u_View", s_Data.CameraBuffer.View);

        // Compute the MVP matrix from the light's point of view
        // CC_CORE_INFO("Renderer3D::BeginScene\n");
        // glm::mat4 depthProjectionMatrix = camera->GetProjection();
         //glm::mat4 depthProjectionMatrix = glm::ortho<float>(-10, 10, -10, 10, 1.0f, 7.5f);
        glm::mat4 depthProjectionMatrix = glm::ortho<float>(-20.0, 20.0, -20.0, 20.0, 1.0f, 40.0f);
         //glm::mat4 depthProjectionMatrix = glm::ortho<float>(-m_ViewportWidth/2, m_ViewportWidth/2, -m_ViewportHeight/2, m_ViewportHeight / 2, 0.1f, 1000.0f);
        // glm::mat4 depthProjectionMatrix = glm::frustum<float>(-1, 1, -1, 1, 1, 7.5);
        glm::mat4 depthViewMatrix = glm::lookAt(glm::vec3(m_LightPos[0], m_LightPos[1], m_LightPos[2]), glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0f, 1.0f, 0.0f));
        //glm::mat4 depthModelMatrix = glm::mat4(1.0);
        m_depthMVP = depthProjectionMatrix * depthViewMatrix;// *depthModelMatrix;

        static glm::mat4 biasMatrix(
            0.5, 0.0, 0.0, 0.0,
            0.0, 0.5, 0.0, 0.0,
            0.0, 0.0, 0.5, 0.0,
            0.5, 0.5, 0.5, 1.0);

        m_depthBiasMVP = biasMatrix * m_depthMVP;

        s_Data.TriangleShader->SetMat4("u_DepthBiasMVP", m_depthBiasMVP);
        s_Data.TriangleShader->SetMat4("u_DepthMVP", m_depthMVP);
        s_Data.TriangleShader->SetFloat4("lightPosition", m_LightPos );
        s_Data.TriangleShader->SetFloat3("lightColor", { m_LightColor[0], m_LightColor[1],m_LightColor[2]});
        s_Data.TriangleShader->SetFloat("lightIntensity", m_LightIntensity);

        glm::vec3 pos = camera->GetPosition();
        s_Data.TriangleShader->SetFloat3("cameraPos", pos);

        s_Data.TriangleShadowShader->Bind();
        s_Data.TriangleShadowShader->SetMat4("u_DepthMVP", m_depthMVP);
        // CC_CORE_INFO("Renderer3D::u_DepthMVP\n");

        StartBatch();
    }

    void Renderer3D::EndScene()
    {
        // CC_CORE_INFO("Renderer3D::EndScene()");
        Flush();
        // CC_CORE_INFO("Renderer3D::EndScene(2)");
        if (s_Data.ShadowDrawState)
        {
            s_Data.FramebufferData->Unbind();
            s_Data.FramebufferDataTransparent->Unbind();
        }
        else if (s_Data.FramebufferDataCustom) {
            s_Data.FramebufferDataCustom->Unbind();
        }
        RenderCommand::SetEnableState(CapEnum_CULL_FACE, false);
        RenderCommand::SetEnableState(CapEnum_GL_DEPTH_TEST, false);
    }

    void Renderer3D::StartBatch()
    {
        s_Data.QuadIndexCount = 0;
        s_Data.QuadVertexBufferPtr = s_Data.QuadVertexBufferBase;

        s_Data.TriangleIndexCount = 0;
        s_Data.TriangleVertexBufferPtr = s_Data.TriangleVertexBufferBase;

        s_Data.LineVertexCount = 0;
        s_Data.LineVertexBufferPtr = s_Data.LineVertexBufferBase;

        s_Data.SkyVertexCount = 0;
        s_Data.SkyVertexBufferPtr = s_Data.SkyVertexBufferBase;
    }

    void Renderer3D::Flush()
    {
        // CC_CORE_INFO(" Renderer3D::Flush\n");
        if (s_Data.QuadIndexCount)
        {
            // CC_CORE_INFO("Flush::QuadIndexCount()");
            constexpr size_t quadVertexCount = 36;

            uint32_t dataSize = sizeof(QuadVertex) * quadVertexCount;
            s_Data.QuadVertexBuffer->SetData(s_Data.QuadVertexBufferBase[0].EntityQuadVertex, dataSize);

            for (uint32_t i = 0; i < s_Data.MaxTextureSlots; i++)
            {
                if (s_Data.TextureSlots[i])
                {
                    s_Data.TextureSlots[i]->Bind(i);
                    s_Data.QuadShader->SetInt("u_Textures[" + Tool::Int2Str(i) + "]", i);
                    // CC_CORE_INFO("QuadIndexCount s_Data.TextureSlots[%d] %d\n", i,  s_Data.TextureSlots[i]->GetID());
                }
            }

            s_Data.QuadShader->Bind();
            s_Data.QuadShader->SetMat4("u_Model", s_Data.QuadVertexBufferBase[0].Model);
            DisplayModesEnum display = s_Data.QuadVertexBufferBase[0].Display != DisplayModesEnum::DisplayModesEnum_NONE ? s_Data.QuadVertexBufferBase[0].Display : s_Data.Display;
            if (display != DisplayModesEnum::DisplayModesEnum_NONE)
            {
                RenderCommand::DrawIndexed(s_Data.QuadVertexArray, s_Data.QuadIndexCount, display);
            }
            RenderCommand::UnBindTexture();
            s_Data.Stats.DrawCalls++;
        }

        if (s_Data.TriangleIndexCount)
        {

            uint32_t dataSize = (uint32_t)((uint8_t *)s_Data.TriangleVertexBufferPtr - (uint8_t *)s_Data.TriangleVertexBufferBase);
            s_Data.TriangleVertexBuffer->SetData(s_Data.TriangleVertexBufferBase, dataSize);

            if (s_Data.ShadowDrawState)
            {
                // CC_CORE_INFO("Flush::TriangleIndexCount(ShadowDrawState)");
                // step 1 render shadow map

                MeshModelMaterialComponent MeshModelMaterial = s_Data.Triangle3dBase[0].MeshModelMaterial;
                if (!MeshModelMaterial.EanbleGenerateShadow) return;

                s_Data.TriangleShadowShader->Bind();

                int size = s_Data.Triangle3dBase[0].FinalBoneMatirces.size();
                for (int i = 0; i < size; ++i)
                {
                    std::string n = "finalBonesMatrices[" + Tool::Int2Str(i) + "]";
                    s_Data.TriangleShadowShader->SetMat4(n, s_Data.Triangle3dBase[0].FinalBoneMatirces[i]);
                }

                s_Data.TriangleShadowShader->SetMat4("u_Model", s_Data.Triangle3dBase[0].Model);

                //glm::mat4 NormalOut = glm::transpose(glm::inverse(s_Data.CameraBuffer.View * s_Data.Triangle3dBase[0].Model));
                glm::mat4 NormalOut = glm::transpose(glm::inverse(s_Data.CameraBuffer.View * s_Data.Triangle3dBase[0].Model));
                s_Data.TriangleShadowShader->SetMat4("NormalOut", NormalOut);

                RenderCommand::DrawIndexed(s_Data.TriangleVertexArray, s_Data.TriangleIndexCount, DisplayModesEnum::DisplayModesEnum_TRIANGLES);
            }
            else
            {
                // CC_CORE_INFO("Flush::TriangleIndexCount()");
                // step 2
                s_Data.TriangleShader->Bind();

                if(s_Data.Triangle3dBase[0].HasAnim){
                    int size = s_Data.Triangle3dBase[0].FinalBoneMatirces.size();
                    for (int i = 0; i < size; ++i)
                    {
                        std::string n = "finalBonesMatrices[" + Tool::Int2Str(i) + "]";
                        s_Data.TriangleShader->SetMat4(n, s_Data.Triangle3dBase[0].FinalBoneMatirces[i]);
                    }
                }   
                s_Data.TriangleShader->SetInt("HasAnim", s_Data.Triangle3dBase[0].HasAnim?1:0);             

                s_Data.TriangleShader->SetMat4("u_Model", s_Data.Triangle3dBase[0].Model);

                glm::mat4 NormalOut = glm::transpose(glm::inverse(s_Data.CameraBuffer.View * s_Data.Triangle3dBase[0].Model));
                s_Data.TriangleShader->SetMat4("NormalOut", NormalOut);

                MeshModelMaterialComponent MeshModelMaterial = s_Data.Triangle3dBase[0].MeshModelMaterial;
                s_Data.TriangleShader->SetInt("hasColorMaterial", MeshModelMaterial.HasColorMaterial);
                s_Data.TriangleShader->SetInt("enableLight", MeshModelMaterial.EanbleLight);
                s_Data.TriangleShader->SetInt("enableDisplayShadow", MeshModelMaterial.EanbleDisplayShadow);

                Texture2D *depthID = s_Data.FramebufferData->GetDepthAttachmentTexture2D();
                Texture2D *depthIDColor = s_Data.FramebufferData->GetColor0AttachmentTexture2D();
                Texture2D *depthIDTransparent = s_Data.FramebufferDataTransparent->GetDepthAttachmentTexture2D();
                Texture2D *depthIDTransparentColor = s_Data.FramebufferDataTransparent->GetColor0AttachmentTexture2D();
                // Texture2D *depthID = s_Data.FramebufferData->GetColor0AttachmentTexture2D();
                // uint32_t depthID2 = s_Data.FramebufferData->GetDepthAttachmentID();

                if (MeshModelMaterial.HasColorMaterial)
                {
                    s_Data.TriangleShader->SetFloat4("material.ambient", MeshModelMaterial.MaterialMeshType.Ambient);
                    s_Data.TriangleShader->SetFloat4("material.diffuse", MeshModelMaterial.MaterialMeshType.Diffuse);
                    s_Data.TriangleShader->SetFloat4("material.specular", MeshModelMaterial.MaterialMeshType.Specular);
                    s_Data.TriangleShader->SetFloat("material.shininess", MeshModelMaterial.MaterialMeshType.Shininess);

                }
                else
                {
                    unsigned int diffuseNr = 1;
                    unsigned int specularNr = 1;
                    unsigned int normalNr = 1;
                    unsigned int heightNr = 1;
                    unsigned int roughnessNr = 1;
                    unsigned int aoNr = 1;

                    size_t texCount = MeshModelMaterial.Textures.size();
                    for (size_t i = 0; i < texCount; i++)
                    {
                        std::string number;
                        Texture2D *t = MeshModelMaterial.Textures[i];
                        std::string name = t->TypeName;
                        if (name == "texture_diffuse")
                            number = Tool::Int2Str(diffuseNr++);
                        else if (name == "texture_specular")
                            number = Tool::Int2Str(specularNr++);
                        else if (name == "texture_normal")
                            number = Tool::Int2Str(normalNr++);
                        else if (name == "texture_height")
                            number = Tool::Int2Str(heightNr++);
                        else if (name == "texture_shininess_roughness")
                            number = Tool::Int2Str(roughnessNr++);
                        else if (name == "texture_opacity_ao")
                            number = Tool::Int2Str(aoNr++);

                        s_Data.TriangleShader->SetInt((name + number), i);
                        t->Bind(i);

                        //if (i == 0) {
                        //    s_Data.TriangleShader->SetFloat2("TextureSize0", glm::vec2(t->GetWidth(), t->GetHeight()));
                        //}

                        /*const int txtIndex = 8;
                        if (name == "texture_diffuse") {
                            s_Data.TriangleShader->SetInt("albedoMap", txtIndex);
                            t->Bind(txtIndex);
                        }
                        else if (name == "texture_height") {
                            s_Data.TriangleShader->SetInt("normalMap", txtIndex + 1);
                            t->Bind(txtIndex + 1);
                        }
                        else if (name == "texture_specular") {
                            s_Data.TriangleShader->SetInt("metallicMap", txtIndex + 2);
                            t->Bind(txtIndex + 2);
                        }
                        else if (name == "texture_shininess_roughness") {
                            s_Data.TriangleShader->SetInt("roughnessMap", txtIndex + 3);
                            t->Bind(txtIndex + 3);
                        }
                        else if (name == "texture_opacity_ao") {
                            s_Data.TriangleShader->SetInt("aoMap", txtIndex + 4);
                            t->Bind(txtIndex + 4);
                        }*/
                    }
  
                }

                s_Data.TriangleShader->SetInt("shadowMap", 9);
                depthID->Bind(9);
                s_Data.TriangleShader->SetInt("shadowMapTransparent", 10);
                depthIDTransparent->Bind(10);
                s_Data.TriangleShader->SetFloat2("TextureSizeShadown", glm::vec2(s_Data.FramebufferData->GetSpecification().Width, s_Data.FramebufferData->GetSpecification().Height));

                s_Data.TriangleShader->SetFloat("BlurRadius", 1.0);
                s_Data.TriangleShader->SetFloat2("BlurDirection", glm::vec2(1.0, 1.0));

                DisplayModesEnum display = s_Data.Triangle3dBase[0].Display != DisplayModesEnum::DisplayModesEnum_NONE ? s_Data.Triangle3dBase[0].Display : s_Data.Display;
                if (display != DisplayModesEnum::DisplayModesEnum_NONE)
                {
                    RenderCommand::DrawIndexed(s_Data.TriangleVertexArray, s_Data.TriangleIndexCount, display);
                }

                RenderCommand::UnBindTexture();
            }
            s_Data.Stats.DrawCalls++;
        }

        if (s_Data.LineVertexCount)
        {
            //  CC_CORE_INFO("Flush::LineVertexCount()");
            uint32_t dataSize = (uint32_t)((uint8_t *)s_Data.LineVertexBufferPtr - (uint8_t *)s_Data.LineVertexBufferBase);
            s_Data.LineVertexBuffer->SetData(s_Data.LineVertexBufferBase, dataSize);

            s_Data.LineShader->Bind();
            RenderCommand::SetLineWidth(s_Data.LineWidth);
            RenderCommand::DrawLines(s_Data.LineVertexArray, s_Data.LineVertexCount);
            s_Data.Stats.DrawCalls++;
        }

        if (s_Data.SkyVertexCount)
        {
            //  CC_CORE_INFO("Flush::LineVertexCount()");
            constexpr size_t vertexCount = 36;

            uint32_t dataSize = vertexCount * sizeof(SkyVertex);// (uint32_t)((uint8_t *)s_Data.SkyVertexBufferPtr - (uint8_t *)s_Data.SkyVertexBufferBase);
            s_Data.SkyVertexBuffer->SetData(s_Data.SkyVertexBufferBase, dataSize);

            s_Data.SkyShader->Bind();
            RenderCommand::SetDepthFunc(DepthFuncEnum_GL_DEPTH_TEST_FUNC_LEQUAL);
            s_Data.SkyShader->SetInt("skybox", 0);
            s_Data.meshCubeMaterialComponent.Texture->Bind();
            RenderCommand::DrawArrays(s_Data.SkyVertexArray, vertexCount);
            RenderCommand::SetDepthFunc(DepthFuncEnum_GL_DEPTH_TEST_FUNC_LESS);

            s_Data.Stats.DrawCalls++;
        }
    }

    void Renderer3D::NextBatch()
    {
        Flush();
        StartBatch();
    }

    void Renderer3D::DrawCube(const glm::vec3 &position, const glm::vec3 &rotation, const glm::vec3 &size, const glm::vec4 &color, int entityID)
    {
        glm::mat4 transform = glm::mat4(1.0);
        transform = Tool::TranslateByLocal(transform, position);
        transform = Tool::RotateByLocalAngle(transform, rotation);
        transform = Tool::ScaleByLocal(transform, size);

        s_Data.QuadVertexBufferPtr[0].Display = DisplayModesEnum::DisplayModesEnum_NONE;
        DrawCube(transform, color, entityID);
    }

    void Renderer3D::DrawModel(const glm::mat4 &transform, MeshModelMaterialComponent &src, MeshComponent &mesh, int entityID)
    {
        // if (s_Data.TriangleIndexCount >= Renderer3DData::MaxTriangleIndices)
        // NextBatch();

        size_t vertexCount = mesh.MeshData.Vertices.size();
        size_t texCount = src.Textures.size();
        size_t idCount = mesh.MeshData.Indices.size();

        CC_ASSERT(idCount < s_Data.MaxTriangleIndices, "ASSERT: Renderer3D::DrawModel idCount(%d) >= s_Data.TriangleIndexCount(%d)\n", idCount, s_Data.TriangleIndexCount);

        // CC_CORE_INFO(" Renderer3D::DrawModel %d %d %d\n", vertexCount, texCount, idCount);

        float textureIndex = 100.0f;

        for (size_t i = 0; i < texCount; i++)
        {
            Texture2D *t = src.Textures[i];
            std::string name = t->TypeName;
            if (name == "texture_diffuse") {
                textureIndex = 0.0f;
                break;
            }
            //if (name == "texture_specular")
            //    textureIndex += 1.0f;
            //if (name == "texture_normal")
            //    textureIndex += 1.0f;
            //if (name == "texture_height")
            //    textureIndex += 1.0f;
            //
            //if (name == "texture_shininess_roughness") {
            //    textureIndex = 5.0f;
            //    break;
            //}
        }

        for (size_t i = 0; i < vertexCount; i++)
        {
            Vertex &v = mesh.MeshData.Vertices[i];
            s_Data.TriangleVertexBufferPtr->Position = v.Position;
            s_Data.TriangleVertexBufferPtr->Color = glm::vec4(1.0);
            s_Data.TriangleVertexBufferPtr->Normal = v.Normal;
            s_Data.TriangleVertexBufferPtr->TexCoord = v.TexCoords;
            s_Data.TriangleVertexBufferPtr->Tangent = v.Tangent;
            s_Data.TriangleVertexBufferPtr->Bitangent = v.Bitangent;
            s_Data.TriangleVertexBufferPtr->BoneIDs[0] = v.m_BoneIDs[0];
            s_Data.TriangleVertexBufferPtr->BoneIDs[1] = v.m_BoneIDs[1];
            s_Data.TriangleVertexBufferPtr->BoneIDs[2] = v.m_BoneIDs[2];
            s_Data.TriangleVertexBufferPtr->BoneIDs[3] = v.m_BoneIDs[3];
            s_Data.TriangleVertexBufferPtr->Weights[0] = v.m_Weights[0];
            s_Data.TriangleVertexBufferPtr->Weights[1] = v.m_Weights[1];
            s_Data.TriangleVertexBufferPtr->Weights[2] = v.m_Weights[2];
            s_Data.TriangleVertexBufferPtr->Weights[3] = v.m_Weights[3];
            s_Data.TriangleVertexBufferPtr->TexIndex = textureIndex;
            s_Data.TriangleVertexBufferPtr->TilingFactor = src.TilingFactor;
            s_Data.TriangleVertexBufferPtr->EntityID = entityID;
            s_Data.TriangleVertexBufferPtr++;

            // CC_CORE_INFO("%d/%d : %f %f %f - %d\n", i , vertexCount, v.Position[0], v.Position[1], v.Position[2],
            // mesh.MeshData.Indices[i]);
        }

        s_Data.Triangle3dBase[0].Model = transform;
        s_Data.Triangle3dBase[0].MeshModelMaterial = src;
        s_Data.Triangle3dBase[0].Display = DisplayModesEnum::DisplayModesEnum_NONE;

        IndexBuffer *IB = s_Data.TriangleVertexArray->GetIndexBuffer();
        IB->SetData((const void *)(mesh.MeshData.Indices.data()), (uint32_t)(idCount * sizeof(unsigned int)));

        s_Data.TriangleIndexCount += idCount;

        s_Data.Stats.TriangleCount += idCount / 3;
    }

    void Renderer3D::DrawModelCustomShader(const glm::mat4 &transform, MeshModelMaterialComponent &src, MeshComponent &mesh, ShaderComponent& shaderc, Entity* entity)
    {
        //auto compoent_mesh = entity->GetComponent<MeshComponent>(ComponentType_Mesh);
        auto& init = mesh.MeshData.Init;
        if (!init) {
            init = true;
            //CC_CORE_INFO("init...");
            if (mesh.MeshData.MultiInstance) {
                mesh.MeshData.SetupMeshInstance(mesh.MeshDataInstance);
            }
            else {
                mesh.MeshData.SetupMesh();
            }
        }

        if (s_Data.ShadowDrawState)
        {

            if (!src.EanbleGenerateShadow) return;

            s_Data.TriangleShadowShader->Bind();

            int size = s_Data.Triangle3dBase[0].FinalBoneMatirces.size();
            for (int i = 0; i < size; ++i)
            {
                std::string n = "finalBonesMatrices[" + Tool::Int2Str(i) + "]";
                s_Data.TriangleShadowShader->SetMat4(n, s_Data.Triangle3dBase[0].FinalBoneMatirces[i]);
            }

            s_Data.TriangleShadowShader->SetMat4("u_Model", transform);

            //glm::mat4 NormalOut = glm::transpose(glm::inverse(s_Data.CameraBuffer.View * s_Data.Triangle3dBase[0].Model));
            glm::mat4 NormalOut = glm::transpose(glm::inverse(s_Data.CameraBuffer.View * transform));
            s_Data.TriangleShadowShader->SetMat4("NormalOut", NormalOut);

            //RenderCommand::DrawIndexed(s_Data.TriangleVertexArray, s_Data.TriangleIndexCount, DisplayModesEnum::DisplayModesEnum_TRIANGLES);

            if (mesh.MeshData.TriangleVertexArray)
            {
                RenderCommand::DrawIndexed(mesh.MeshData.TriangleVertexArray,
                    mesh.MeshData.TriangleIndexCount, DisplayModesEnum_TRIANGLES);
            }
        }
        else {

            shaderc.shader->Bind();
            shaderc.shader->SetMat4("u_Projection", s_Data.CameraBuffer.Projection);
            shaderc.shader->SetMat4("u_View", s_Data.CameraBuffer.View);

            shaderc.shader->SetFloat4("lightPosition", m_LightPos);
            shaderc.shader->SetFloat3("lightColor", { m_LightColor[0], m_LightColor[1],m_LightColor[2] });
            shaderc.shader->SetFloat("lightIntensity", m_LightIntensity);

            shaderc.shader->SetFloat3("cameraPos", s_Data.CameraBuffer.Position);

            shaderc.shader->SetInt("HasAnim", s_Data.Triangle3dBase[0].HasAnim ? 1 : 0);

            shaderc.shader->SetMat4("u_Model", transform);

            glm::mat4 NormalOut = glm::transpose(glm::inverse(s_Data.CameraBuffer.View * transform));
            shaderc.shader->SetMat4("NormalOut", NormalOut);

            shaderc.shader->SetInt("hasColorMaterial", src.HasColorMaterial);
            shaderc.shader->SetInt("enableLight", src.EanbleLight);
            shaderc.shader->SetInt("enableDisplayShadow", src.EanbleDisplayShadow);

            shaderc.shader->SetMat4("u_DepthBiasMVP", m_depthBiasMVP);
            shaderc.shader->SetMat4("u_DepthMVP", m_depthMVP);

            Texture2D *depthID = s_Data.FramebufferData->GetDepthAttachmentTexture2D();
            Texture2D *depthIDColor = s_Data.FramebufferData->GetColor0AttachmentTexture2D();
            Texture2D *depthIDTransparent = s_Data.FramebufferDataTransparent->GetDepthAttachmentTexture2D();
            Texture2D *depthIDTransparentColor = s_Data.FramebufferDataTransparent->GetColor0AttachmentTexture2D();

            size_t texCount = 0;
            if (src.HasColorMaterial)
            {
                shaderc.shader->SetFloat4("material.ambient", src.MaterialMeshType.Ambient);
                shaderc.shader->SetFloat4("material.diffuse", src.MaterialMeshType.Diffuse);
                shaderc.shader->SetFloat4("material.specular", src.MaterialMeshType.Specular);
                shaderc.shader->SetFloat("material.shininess", src.MaterialMeshType.Shininess);

            }
            else
            {
                unsigned int diffuseNr = 1;
                unsigned int specularNr = 1;
                unsigned int normalNr = 1;
                unsigned int heightNr = 1;
                unsigned int roughnessNr = 1;
                unsigned int aoNr = 1;

                texCount = src.Textures.size();
                for (size_t i = 0; i < texCount; i++)
                {
                    std::string number;
                    Texture2D *t = src.Textures[i];
                    std::string name = t->TypeName;
                    if (name == "texture_diffuse")
                        number = Tool::Int2Str(diffuseNr++);
                    else if (name == "texture_specular")
                        number = Tool::Int2Str(specularNr++);
                    else if (name == "texture_normal")
                        number = Tool::Int2Str(normalNr++);
                    else if (name == "texture_height")
                        number = Tool::Int2Str(heightNr++);
                    else if (name == "texture_shininess_roughness")
                        number = Tool::Int2Str(roughnessNr++);
                    else if (name == "texture_opacity_ao")
                        number = Tool::Int2Str(aoNr++);

                    shaderc.shader->SetInt((name + number), i);
                    t->Bind(i);

                    /*const int txtIndex = 8;
                    if (name == "texture_diffuse") {
                        s_Data.TriangleShader->SetInt("albedoMap", txtIndex);
                        t->Bind(txtIndex);
                    }
                    else if (name == "texture_height") {
                        s_Data.TriangleShader->SetInt("normalMap", txtIndex + 1);
                        t->Bind(txtIndex + 1);
                    }
                    else if (name == "texture_specular") {
                        s_Data.TriangleShader->SetInt("metallicMap", txtIndex + 2);
                        t->Bind(txtIndex + 2);
                    }
                    else if (name == "texture_shininess_roughness") {
                        s_Data.TriangleShader->SetInt("roughnessMap", txtIndex + 3);
                        t->Bind(txtIndex + 3);
                    }
                    else if (name == "texture_opacity_ao") {
                        s_Data.TriangleShader->SetInt("aoMap", txtIndex + 4);
                        t->Bind(txtIndex + 4);
                    }*/
                }
            }

            shaderc.shader->SetInt("shadowMap", 9);
            shaderc.shader->SetInt("shadowMapTransparent", 10);
            depthID->Bind(9);
            depthIDTransparent->Bind(10);
            shaderc.shader->SetFloat2("TextureSizeShadown", glm::vec2(s_Data.FramebufferData->GetSpecification().Width, s_Data.FramebufferData->GetSpecification().Height));

            shaderc.shader->SetFloat("BlurRadius", 1.0);
            shaderc.shader->SetFloat2("BlurDirection", glm::vec2(1.0, 1.0));

            size_t texCountCustom = texCount;
            if (entity->HasComponent(ComponentType_Custom3DMaterial)) {
                auto custom3DMaterial = entity->GetComponent<Custom3DMaterialComponent>(ComponentType_Custom3DMaterial);
                std::unordered_map<std::string, Custom3DMaterialComponent::DataInfo>::iterator it = custom3DMaterial->datas.begin();
                for (it; it != custom3DMaterial->datas.end(); ++it)
                {
                    if (Custom3DMaterialComponent::DataType_Int == it->second.dataType) {
                        shaderc.shader->SetInt(it->first, it->second.a4[0]);
                    }
                    else if (Custom3DMaterialComponent::DataType_Int2 == it->second.dataType) {
                        shaderc.shader->SetIntArray(it->first, &it->second.a4[0], 2);
                    }
                    else if (Custom3DMaterialComponent::DataType_Int3 == it->second.dataType) {
                        shaderc.shader->SetIntArray(it->first, &it->second.a4[0], 3);
                    }
                    else if (Custom3DMaterialComponent::DataType_Int4 == it->second.dataType) {
                        shaderc.shader->SetIntArray(it->first, &it->second.a4[0], 4);
                    }
                    else if (Custom3DMaterialComponent::DataType_Float == it->second.dataType) {
                        shaderc.shader->SetFloat(it->first, it->second.b4[0]);
                    }
                    else if (Custom3DMaterialComponent::DataType_Float2 == it->second.dataType) {
                        shaderc.shader->SetFloat2(it->first, glm::vec2(it->second.b4[0], it->second.b4[1]));
                    }
                    else if (Custom3DMaterialComponent::DataType_Float3 == it->second.dataType) {
                        shaderc.shader->SetFloat3(it->first, glm::vec3(it->second.b4[0], it->second.b4[1], it->second.b4[2]));
                    }
                    else if (Custom3DMaterialComponent::DataType_Float4 == it->second.dataType) {
                        shaderc.shader->SetFloat4(it->first, glm::vec4(it->second.b4[0], it->second.b4[1], it->second.b4[3], it->second.b4[4]));
                    }
                    else if (Custom3DMaterialComponent::DataType_Texture == it->second.dataType) {
                        Texture2D* tex = it->second.texture2D;
                        shaderc.shader->SetInt(it->first, texCountCustom);
                        tex->Bind(texCountCustom);
                        texCountCustom += 1;
                    }
                }

            }

            // DisplayModesEnum display = s_Data.Triangle3dBase[0].Display != DisplayModesEnum::DisplayModesEnum_NONE ? s_Data.Triangle3dBase[0].Display : s_Data.Display;
             //if (display != DisplayModesEnum::DisplayModesEnum_NONE)
            if (mesh.MeshData.TriangleVertexArray)
            {
                RenderCommand::DrawIndexed(mesh.MeshData.TriangleVertexArray,
                    mesh.MeshData.TriangleIndexCount, DisplayModesEnum_TRIANGLES);
            }

            RenderCommand::UnBindTexture();
        }
    }

    void Renderer3D::DrawModelNoAnim(const glm::mat4 &transform, MeshModelMaterialComponent &src, MeshComponent &mesh, int entityID)
    {
        NextBatch();
        s_Data.Triangle3dBase[0].HasAnim = false;
        DrawModel(transform, src, mesh, entityID);
    }

    void Renderer3D::DrawModelNoAnimCustomShader(const glm::mat4 &transform, MeshModelMaterialComponent &src, MeshComponent &mesh, ShaderComponent& shaderc, Entity* entity)
    {
        NextBatch();
        s_Data.Triangle3dBase[0].HasAnim = false;
        DrawModelCustomShader(transform, src, mesh, shaderc, entity);
    }

    void Renderer3D::DrawModelAnim(const glm::mat4 &transform, MeshModelMaterialComponent &src, MeshComponent &mesh, AnimationComponent &anim, int entityID)
    {
        NextBatch();
        s_Data.Triangle3dBase[0].HasAnim = true;
        {
            //
            auto transforms = anim.AnimatorObject->GetFinalBoneMatrices();
            s_Data.Triangle3dBase[0].FinalBoneMatirces.assign(transforms.begin(), transforms.end());
        }
        DrawModel(transform, src, mesh, entityID);
    }

    void Renderer3D::DrawModelAnimCustomShader(const glm::mat4 &transform, MeshModelMaterialComponent &src, MeshComponent &mesh, AnimationComponent &anim, ShaderComponent& shaderc, Entity* entity)
    {
        NextBatch();

        s_Data.Triangle3dBase[0].HasAnim = true;

        auto transforms = anim.AnimatorObject->GetFinalBoneMatrices();
        if (s_Data.ShadowDrawState)
        {
            s_Data.Triangle3dBase[0].FinalBoneMatirces.assign(transforms.begin(), transforms.end());
        }
        else {
            shaderc.shader->Bind();

            int size = transforms.size();
            for (int i = 0; i < size; ++i)
            {
                std::string n = "finalBonesMatrices[" + Tool::Int2Str(i) + "]";
                shaderc.shader->SetMat4(n, transforms[i]);
            }
        }

        DrawModelCustomShader(transform, src, mesh, shaderc, entity);

    }

    void Renderer3D::DrawBoxBorder(const glm::vec3 &position, const glm::vec3 &rotation, const glm::vec3 &size, glm::vec4 &color)
    {
        glm::mat4 transform = glm::mat4(1.0);
        transform = Tool::TranslateByLocal(transform, position);
        transform = Tool::RotateByLocalRadians(transform, rotation);
        transform = Tool::ScaleByLocal(transform, size);

        DrawCube(transform, color);
        s_Data.QuadVertexBufferPtr[0].Display = DisplayModesEnum::DisplayModesEnum_LINE_LOOP;
    }

    void Renderer3D::DrawBoxBorder(const glm::mat4 &transform, glm::vec4 &color)
    {
        DrawCube(transform, color);
        s_Data.QuadVertexBufferPtr[0].Display = DisplayModesEnum::DisplayModesEnum_LINE_LOOP;
    }

    //void Renderer3D::DrawTriangleBorder(const glm::vec3 &position, const glm::vec3 &rotation,
    //    const unsigned char* vertexBase, const int vertexStride, const int numVertices,
    //    const unsigned char* triangleIndexBase, const int triangleIndexStride, const int numTriangles,
    //    glm::vec4 &color)
    //{
    //    glm::mat4 transform = glm::mat4(1.0);
    //    transform = Tool::TranslateByLocal(transform, position);
    //    transform = Tool::RotateByLocalAngle(transform, rotation);
    //    //transform = Tool::ScaleByLocal(transform, size);

    //    
    //    MeshModelMaterialComponent src;
    //    src.HasColorMaterial = true;
    //    src.EanbleLight = false;
    //    src.MaterialMeshType.Diffuse = color;

    //    MeshComponent mesh;
    //    glm::vec3 lastPosition = glm::vec3(0.0);
    //    for (int i = 0; i < numVertices*3; i+=3) {
    //        Vertex v;
    //        v.Position = { vertexBase[i], vertexBase[i + 1], vertexBase[i + 2] };
    //        mesh.MeshData.Vertices.push_back(v);
    //        glm::vec4 p = transform * glm::vec4(vertexBase[i], vertexBase[i + 1], vertexBase[i + 2], 1.0);
    //        glm::vec3 Position = { p[0], p[1], p[2] };
    //        if (i > 0) {
    //            DrawLine(lastPosition, Position, color);
    //        }
    //        lastPosition = Position;
    //       
    //    }
    //    for (int i = 0; i < numTriangles*3; i++) {
    //       mesh.MeshData.Indices.push_back((unsigned short)(triangleIndexBase[i]));
    //    }
    //    
    //    DrawModelNoAnim(transform, src, mesh);
    //    s_Data.Triangle3dBase[0].Display = DisplayModesEnum::DisplayModesEnum_LINE_LOOP;
    //}

    void Renderer3D::DrawTriangleBorder(const glm::mat4 &transform, MeshModelMaterialComponent &src, MeshComponent &mesh, int entityID)
    {
        DrawModelNoAnim(transform, src, mesh, entityID);
        s_Data.Triangle3dBase[0].Display = DisplayModesEnum::DisplayModesEnum_LINE_LOOP;
    }

    void Renderer3D::DrawCube(const glm::vec3 &position, const glm::vec3 &rotation, const glm::vec3 &size, Texture2D *texture, float tilingFactor, const glm::vec4 &tintColor)
    {
        glm::mat4 transform = glm::mat4(1.0);
        transform = Tool::TranslateByLocal(transform, position);
        transform = Tool::RotateByLocalAngle(transform, rotation);
        transform = Tool::ScaleByLocal(transform, size);

        DrawCube(transform, texture, tilingFactor, tintColor);
    }

    void Renderer3D::DrawCube(const glm::mat4 &transform, Texture2D *texture, float tilingFactor, const glm::vec4 &tintColor, int entityID)
    {
        NextBatch();

        constexpr size_t quadVertexCount = 36;
        const float textureIndex = 0.0f; // White Texture

        s_Data.TextureSlots[0] = texture;

        for (size_t i = 0; i < quadVertexCount; i++)
        {
            s_Data.QuadVertexBufferPtr[0].EntityQuadVertex[i].Position = s_Data.QuadVertexPositions[i];
            s_Data.QuadVertexBufferPtr[0].EntityQuadVertex[i].Color = tintColor;
            s_Data.QuadVertexBufferPtr[0].EntityQuadVertex[i].TexCoord = s_Data.TextureCoords[i];
            s_Data.QuadVertexBufferPtr[0].EntityQuadVertex[i].TexIndex = textureIndex;
            s_Data.QuadVertexBufferPtr[0].EntityQuadVertex[i].TilingFactor = tilingFactor;
            s_Data.QuadVertexBufferPtr[0].EntityQuadVertex[i].EntityID = entityID;
        }
        s_Data.QuadVertexBufferPtr[0].Model = transform;
        s_Data.QuadVertexBufferPtr[0].Display = DisplayModesEnum::DisplayModesEnum_NONE;

        s_Data.QuadIndexCount += quadVertexCount;

        s_Data.Stats.QuadCount++;
    }

    void Renderer3D::DrawCube(const glm::mat4 &transform, const glm::vec4 &color, int entityID)
    {
        NextBatch();

        constexpr size_t quadVertexCount = 36;
        const float textureIndex = 100.0f; // White Texture

        const float tilingFactor = 1.0f;

        for (uint32_t i = 0; i < s_Data.MaxTextureSlots; i++)
        {
            s_Data.TextureSlots[i] = nullptr;
        }

        for (size_t i = 0; i < quadVertexCount; i++)
        {
            s_Data.QuadVertexBufferPtr[0].EntityQuadVertex[i].Position = s_Data.QuadVertexPositions[i];
            s_Data.QuadVertexBufferPtr[0].EntityQuadVertex[i].Color = color;
            s_Data.QuadVertexBufferPtr[0].EntityQuadVertex[i].TexCoord = s_Data.TextureCoords[i];
            s_Data.QuadVertexBufferPtr[0].EntityQuadVertex[i].TexIndex = textureIndex;
            s_Data.QuadVertexBufferPtr[0].EntityQuadVertex[i].TilingFactor = tilingFactor;
            s_Data.QuadVertexBufferPtr[0].EntityQuadVertex[i].EntityID = entityID;
        }
        s_Data.QuadVertexBufferPtr[0].Model = transform;

        s_Data.QuadIndexCount += quadVertexCount;

        s_Data.Stats.QuadCount++;
    }

    void Renderer3D::DrawSprite(const glm::mat4 &transform, MeshBasicMaterialComponent &src, int entityID)
    {
        if (src.Texture)
            DrawCube(transform, src.Texture, src.TilingFactor, src.Color, entityID);
        else
            DrawCube(transform, src.Color, entityID);
    }

    float Renderer3D::GetLineWidth()
    {
        return s_Data.LineWidth;
    }

    void Renderer3D::SetLineWidth(float width)
    {
        s_Data.LineWidth = width;
    }

    void Renderer3D::DrawLine(const glm::vec3 &start, const glm::vec3 &end, const glm::vec4 &color, int entityID)
    {
        NextBatch();

        glm::vec4 p0 = s_Data.CameraBuffer.ViewProjection * glm::vec4(start[0], start[1], start[2], 1.0);
        glm::vec4 p1 = s_Data.CameraBuffer.ViewProjection * glm::vec4(end[0], end[1], end[2], 1.0);

        s_Data.LineVertexBufferPtr->Position = glm::vec3(p0[0], p0[1], p0[2]);
        s_Data.LineVertexBufferPtr->Color = color;
        s_Data.LineVertexBufferPtr->EntityID = entityID;
        s_Data.LineVertexBufferPtr++;

        s_Data.LineVertexBufferPtr->Position = glm::vec3(p1[0], p1[1], p1[2]);
        s_Data.LineVertexBufferPtr->Color = color;
        s_Data.LineVertexBufferPtr->EntityID = entityID;
        s_Data.LineVertexBufferPtr++;

        s_Data.LineVertexCount += 2;
    }

    void Renderer3D::DrawSkybox(const glm::mat4 &transform, const MeshCubeMaterialComponent & src, int entityID)
    {
        NextBatch();

        constexpr size_t vertexCount = 36;
        for (size_t i = 0; i < vertexCount; i++)
        {
            s_Data.SkyVertexBufferPtr[i].Position = s_Data.SkyVertexPositions[i];
            s_Data.SkyVertexBufferPtr[i].Color = src.Color;
            s_Data.SkyVertexBufferPtr[i].EntityID = entityID;
        }

        s_Data.meshCubeMaterialComponent = src;

        s_Data.SkyVertexCount += vertexCount;
    }

    void Renderer3D::SetDisplayMode(DisplayModesEnum mode)
    {
        s_Data.Display = mode;
    }

    void Renderer3D::OnViewportResize(uint32_t x, uint32_t y, uint32_t width, uint32_t height)
    {
        if (width == 0 && height == 0)
            return;

        m_ViewportX = x;
        m_ViewportY = y;
        m_ViewportWidth = width;
        m_ViewportHeight = height;
        RenderCommand::SetViewport(x, y, m_ViewportWidth, m_ViewportHeight);
        //CC_CORE_INFO("Renderer3D::OnViewportResize %d %d\n", m_ViewportWidth, m_ViewportHeight);
        if (s_Data.FramebufferData)
        {
            FramebufferSpecification spec = s_Data.FramebufferData->GetSpecification();
            if (width > 0 && height > 0 && // zero sized framebuffer is invalid
                (spec.Width != width || spec.Height != height))
            {
                s_Data.FramebufferData->Resize(width, height);
            }
        }

        if (s_Data.FramebufferDataTransparent)
        {
            FramebufferSpecification spec = s_Data.FramebufferDataTransparent->GetSpecification();
            if (width > 0 && height > 0 && // zero sized framebuffer is invalid
                (spec.Width != width || spec.Height != height))
            {
                s_Data.FramebufferDataTransparent->Resize(width, height);
            }
        }
    }

    void Renderer3D::SetShadowDrawState(bool s)
    {
        s_Data.ShadowDrawState = s;
    }

    Texture2D *Renderer3D::GetDepthAttachmentTexture2D()
    {
        if (!s_Data.FramebufferData) return NULL;
        return s_Data.FramebufferData->GetDepthAttachmentTexture2D();
    }

    Texture2D *Renderer3D::GetColor0AttachmentTexture2D()
    {
        if (!s_Data.FramebufferData) return NULL;
        return s_Data.FramebufferData->GetColor0AttachmentTexture2D();
    }

    Texture2D *Renderer3D::GetTransparentDepthAttachmentTexture2D()
    {
        if (!s_Data.FramebufferDataTransparent) return NULL;
        return s_Data.FramebufferDataTransparent->GetDepthAttachmentTexture2D();
    }

    Texture2D *Renderer3D::GetTransparentColor0AttachmentTexture2D()
    {
        if (!s_Data.FramebufferDataTransparent) return NULL;
        return s_Data.FramebufferDataTransparent->GetColor0AttachmentTexture2D();
    }

    Texture2D *Renderer3D::GetCustomDepthAttachmentTexture2D()
    {
        if (!s_Data.FramebufferDataCustom) return NULL;
        return s_Data.FramebufferDataCustom->GetDepthAttachmentTexture2D();
    }

    Texture2D *Renderer3D::GetCustomColor0AttachmentTexture2D()
    {
        if (!s_Data.FramebufferDataCustom) return NULL;
        return s_Data.FramebufferDataCustom->GetColor0AttachmentTexture2D();
    }

    void Renderer3D::ResetStats()
    {
        memset(&s_Data.Stats, 0, sizeof(Statistics));
    }

    Renderer3D::Statistics Renderer3D::GetStats()
    {
        return s_Data.Stats;
    }

    FramebufferSpecification Renderer3D::GetSpecification()
    {
        if (!s_Data.FramebufferData) return FramebufferSpecification();
        FramebufferSpecification spec = s_Data.FramebufferData->GetSpecification();
        return spec;
    }
}
